// Copyright (c) XiGuan 2023
// Author： xiguan
// Email: xiguan.teng@qq.com
// Create on 2023/10/19
// TODO:
//

#include "Buffer.h"

#include <sys/uio.h>
#include <unistd.h>

#include <cassert>
#include <cstring>

Buffer::Buffer(int _init_buffer_size)
    : buffer_(_init_buffer_size), read_pos_(0), write_pos_(0) {}

size_t Buffer::WritableBytes() const { return buffer_.size() - write_pos_; }

size_t Buffer::ReadableBytes() const { return write_pos_ - read_pos_; }

size_t Buffer::PrependableBytes() const { return read_pos_; }

char *Buffer::BeginPtr_() { return &(*buffer_.begin()); }

const char *Buffer::BeginPtr_() const { return &(*buffer_.begin()); }

void Buffer::MakeSpace_(size_t len) {
  // also if (buffer_.size() < len)
  if (WritableBytes() + PrependableBytes() < len) {
    buffer_.resize(write_pos_ + len + 1);
  } else {
    size_t readable = ReadableBytes();
    std::copy(BeginPtr_() + read_pos_, BeginPtr_() + write_pos_, BeginPtr_());
    read_pos_ = 0;
    write_pos_ = read_pos_ + readable;
    assert(readable == ReadableBytes());
  }
}

const char *Buffer::Peek() const { return BeginPtr_() + read_pos_; }

void Buffer::EnsureWriteable(size_t len) {
  if (WritableBytes() < len) {
    MakeSpace_(len);
  }
  assert(WritableBytes() >= len);
}

void Buffer::HasWritten(size_t len) { write_pos_ += len; }

void Buffer::Retrieve(size_t len) {
  assert(len <= ReadableBytes());
  read_pos_ += len;
}

void Buffer::RetrieveUntil(const char *end) {
  assert(Peek() <= end);
  Retrieve(end - Peek());
}

void Buffer::RetrieveAll() {
  bzero(&(buffer_[0]), buffer_.size());
  read_pos_ = 0;
  write_pos_ = 0;
}

std::string Buffer::RetrieveAllToStr() {
  std::string str(Peek(), ReadableBytes());
  RetrieveAll();
  return str;
}

const char *Buffer::BeginWriteConst() const { return BeginPtr_() + write_pos_; }

char *Buffer::BeginWrite() { return BeginPtr_() + write_pos_; }

void Buffer::Append(const std::string &str) {
  // data 返回首地址，但不包括 \0
  // c_str 返回首地址，但包括 \0
  Append(str.data(), str.length());
}

void Buffer::Append(const char *str, size_t len) {
  assert(str);
  EnsureWriteable(len);
  std::copy(str, str + len, BeginWrite());
  HasWritten(len);
}

void Buffer::Append(const void *data, size_t len) {
  assert(data);
  Append(static_cast<const char *>(data), len);
}

void Buffer::Append(const Buffer &buffer) {
  Append(buffer.Peek(), buffer.ReadableBytes());
}

ssize_t Buffer::ReadFd(int fd, int *_errno) {
  char temp_buffer[65535] = {0};
  struct iovec iov[2];
  const size_t writeable = WritableBytes();

  // 分散读取，保证数据全部读完
  iov[0].iov_base = BeginPtr_() + write_pos_;
  iov[0].iov_len = writeable;
  iov[1].iov_base = temp_buffer;
  iov[1].iov_len = sizeof(temp_buffer);

  const ssize_t len = readv(fd, iov, 2);
  if (len < 0) {
    *_errno = errno;
  } else if (static_cast<size_t>(len) <= writeable) {
    write_pos_ += len;
  } else {
    write_pos_ = buffer_.size();
    Append(temp_buffer, len - writeable);
  }
  return len;
}

ssize_t Buffer::WriteFd(int fd, int *_errno) {
  size_t read_size = ReadableBytes();
  ssize_t len = write(fd, Peek(), read_size);
  if (len < 0) {
    *_errno = errno;
    return len;
  }
  read_pos_ += len;
  return len;
}
